home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 14 / CU Amiga Magazine's Super CD-ROM 14 (1997)(EMAP Images)(GB)(Track 1 of 3)[!][issue 1997-09].iso / CUCD / Programming / SAS-C / sc655pch / guiprof / guiprof.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-01-26  |  14.9 KB  |  578 lines

  1. /*-------------------------------------------------------------------*/
  2. /* Copyright (c) 1993 by SAS Institute Inc., Cary NC                 */
  3. /* All Rights Reserved                                               */
  4. /*                                                                   */
  5. /* SUPPORT:    walker - Doug Walker                                  */
  6. /*-------------------------------------------------------------------*/
  7. #define __USE_SYSBASE 1
  8.  
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <stdlib.h>
  12. #include <time.h>
  13. #include <assert.h>
  14. #include <exec/ports.h>
  15. #include <dos/dosextens.h>
  16. #include <dos/dostags.h>
  17. #include <exec/execbase.h>
  18. #include <proto/exec.h>
  19. #include <proto/dos.h>
  20. #include <dos.h>
  21. #include "guiprofpriv.h"
  22.  
  23. #define V37 (SysBase->LibNode.lib_Version > 36) 
  24.  
  25. char nullid[] = "";
  26.  
  27. int broken;
  28. int axis;
  29. int sortby = OPT_SORTNUM;
  30.  
  31. void __regargs __chkabort(void){}
  32.  
  33. #if DODEBUG
  34. int dodebug;
  35. #endif
  36.  
  37. int autoexit;
  38.  
  39. static const char __ver[] = "$VER: GUIPROF 6.55 " __AMIGADATE__;
  40.  
  41. struct MsgPort *port;
  42.  
  43. struct SPDAT *spdat;
  44. int spcur, spmax;
  45.  
  46. struct GPInfo **GPInfo;
  47. int GPCur;
  48. int GPMax;
  49. int verbose;
  50. int fullnames;
  51.  
  52. char __stdiowin[] = "CON:0/0/639/199/";
  53.  
  54. unsigned long process;
  55. static void spdoexit(SPM msg);
  56.  
  57. void _STDcleanup(void)
  58. {
  59.    if(port)
  60.    {
  61.       DeletePort(port);
  62.       port = NULL;
  63.    }
  64. }
  65.  
  66. void usage(void)
  67. {
  68.    fprintf(stderr, "USAGE: guiprof [<guiprof arguments>] [<program> [<program arguments>]]\n");
  69.    fprintf(stderr, "guiprof arguments:\n");
  70.    fprintf(stderr, "       WAIT:      Wait for a program compiled with PROFILE to be run.\n");
  71.    fprintf(stderr, "    VERBOSE:      Print detailed trace to stdout (VERY SLOW).\n");
  72.    fprintf(stderr, "  FULLNAMES:      Print long version of function names\n");
  73.    fprintf(stderr, "      FORCE:      Force reports even if it steals time from program.\n");
  74.    fprintf(stderr, "       AXIS:      Print horizontal axis\n");
  75.    fprintf(stderr, "   INTERVAL=time: Report interval, in milliseconds.  Default 250.\n");
  76.    fprintf(stderr, "       SORT=type: Either ALPHA or NUM, for alphabetic or numeric sort.\n");
  77.    fprintf(stderr, "     FORMAT=fmt:  Report format.  Format is one of\n"
  78.                    "        PCT:   Percent of time spent in routine.\n"
  79.                    "        ETIME: Milliseconds in routine, excluding subroutines.\n"
  80.                    "        ITIME: Milliseconds in routine, including subroutines.\n"
  81.                    "        COUNT: Number of times routine was called.\n"
  82.           );
  83.                    
  84.    exit(20);
  85. }
  86.  
  87. int main(int argc, char *argv[])
  88. {
  89.    SPM msg;
  90.    int i, len;
  91.    int keepon = 1;
  92.    char *modname = NULL;
  93.    char *cmd, *p;
  94.    int wait = 0;
  95.    int reporttime = 250; /* Every quarter-second */
  96.    int doreport = 0;
  97.    int forcereport = 0;
  98.    long waitbits, timerbit, res;
  99.    sptime now = 0;
  100.    struct ExecBase *SysBase = *(struct ExecBase **)4;
  101.  
  102.    if(!V37)
  103.    {
  104.       fprintf(stderr, "This program requires AmigaDOS 2.0 or higher\n");
  105.       /* If we were run from the 1.3 WorkBench, our stderr window will */
  106.       /* close when we exit, so delay for 4 seconds.                   */
  107.       if(argv == NULL) Delay(200);
  108.       exit(20);
  109.    }
  110.  
  111.    /* Parse workbench arguments */
  112.    if(argc == 0)
  113.    {
  114.       /* See <dos.h> */
  115.       argc = _WBArgc;
  116.       argv = _WBArgv;
  117.    }
  118.  
  119.    timerbit = waitbits = OpenTimer();
  120.  
  121.    if(waitbits == 0)
  122.    {
  123.       fprintf(stderr, "ERROR: Can't open timer.device\n");
  124.       exit(20);
  125.    }
  126.  
  127.    waitbits |= (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_F);
  128.  
  129.    /* We can't use ReadArgs because our parameters are positional. */
  130.    /* We want arguments specified after the program name to be     */
  131.    /* program arguments, and ReadArgs doesn't support this.        */
  132.    if(argc)
  133.    {
  134.       while(argv[1])
  135.       {
  136.          if(!strcmp(argv[1], "?"))
  137.             usage();
  138.          else if(!stricmp(argv[1], "WAIT")) 
  139.             wait = 1;
  140.          else if(!stricmp(argv[1], "AXIS"))
  141.             axis = 1;
  142.          else if(!strnicmp(argv[1], "INTERVAL", 8))
  143.          {
  144.             if(argv[1][8] && argv[1][8] != '=')
  145.                usage();
  146.  
  147.             if(argv[1][8] == '=')
  148.             {
  149.                if(stcd_i(argv[1]+9, &reporttime) <= 0)
  150.                   usage();
  151.             }
  152.             else
  153.             {
  154.                if(argv[2] == NULL || stcd_i(argv[2], &reporttime) <= 0)
  155.                   usage();
  156.                argv++;
  157.             }
  158.          }
  159.          else if(!stricmp(argv[1], "VERBOSE"))
  160.          {
  161.             verbose = 1;
  162.          }
  163.          else if(!stricmp(argv[1], "FULLNAMES"))
  164.          {
  165.             fullnames = 1;
  166.          }
  167.          else if(!stricmp(argv[1], "FORCE"))
  168.          {
  169.             /* Produce reports even at the expense of the program */
  170.             /* being profiled.  If FORCE is not set, reports will */
  171.             /* only be produced when the program is suspended.    */
  172.             forcereport = 1;
  173.          }
  174.          else if(!strnicmp(argv[1], "SORT", 4))
  175.          {
  176.             if(argv[1][4] == 0)
  177.             {
  178.                argv++, argc--;
  179.                p = argv[1];
  180.             }
  181.             else if(argv[1][4] == '=')
  182.                p = argv[1]+5;
  183.             else
  184.                usage();
  185.                
  186.             if(!stricmp(p, "ALPHA"))
  187.                sortby = OPT_SORTALPHA;
  188.             else if(!stricmp(p, "NUM"))
  189.                sortby = OPT_SORTNUM;
  190.             else
  191.                usage();
  192.          }
  193.          else if(!strnicmp(argv[1], "FORMAT", 6))
  194.          {
  195.             if(argv[1][6] == 0)
  196.             {
  197.                argv++, argc--;
  198.                p = argv[1];
  199.             }
  200.             else if(argv[1][6] == '=')
  201.                p = argv[1]+7;
  202.             else
  203.                usage();
  204.  
  205.             if(!stricmp(p, "PCT"))
  206.             {
  207.                report_type = RPT_PCT;
  208.             }
  209.             else if(!stricmp(p, "COUNT"))
  210.             {
  211.                report_type = RPT_CNT;
  212.             }
  213.             else if(!stricmp(p, "ITIME"))
  214.             {
  215.                report_type = RPT_ITIME;
  216.             }
  217.             else if(!stricmp(p, "ETIME"))
  218.             {
  219.                report_type = RPT_ETIME;
  220.             }
  221.          }
  222. #if DODEBUG
  223.          else if(!stricmp(argv[1], "DEBUG"))
  224.          {
  225.             dodebug=1;
  226.          }
  227. #endif
  228.          else if(!stricmp(argv[1], "AUTOEXIT"))
  229.          {
  230.             autoexit = 1;
  231.          }
  232.          else
  233.          {
  234.             modname = argv[1];
  235.             argv++;
  236.             break; /* Args after this belong to program */
  237.          }
  238.  
  239.          argv++;
  240.       }
  241.    }
  242.    if(InitReport()) exit(20);
  243.  
  244.    if(!modname && !wait)
  245.    {
  246.       fprintf(stderr, "You must specify either WAIT or a program name\n");
  247.       exit(99);
  248.    }
  249.  
  250.    /* Check to make sure there isn't already another SPROF or GUIPROF */
  251.    /* process running.  If there isn't, go ahead and allocate our     */
  252.    /* MsgPort. Since it's possible that one would start up after the  */
  253.    /* call to FindPort() but before the call to CreatePort(), we do   */
  254.    /* the FindPort()/CreatePort() pair under Forbid().                */
  255.    Forbid();
  256.    if(FindPort(SPROFPORT))
  257.    {
  258.       Permit();
  259.       fprintf(stderr, "Message port \"" SPROFPORT "\" already open\n");
  260.       exit(99);
  261.    }
  262.  
  263.    port = CreatePort(SPROFPORT, 0L);
  264.    Permit();
  265.  
  266.    if(port == NULL)
  267.    {
  268.       fprintf(stderr, "Can't allocate message port \"" SPROFPORT "\"\n");
  269.       exit(99);
  270.    }
  271.  
  272.    waitbits |= (1<<port->mp_SigBit);
  273.  
  274.    if(wait)
  275.    {
  276.       printf("GUIPROF: Please run the program to be profiled now.\n");
  277.    }
  278.    else
  279.    {
  280.       BPTR fh1, fh2;
  281.  
  282.       for(i=1, len=strlen(modname) + 2; argv[i]; i++)
  283.          len += strlen(argv[i]) + 1;
  284.  
  285.       if(!(cmd = malloc(len)))
  286.       {
  287.          fprintf(stderr, "Can't allocate %d bytes!\n", len);
  288.          DeletePort(port);
  289.          exit(20);
  290.       }
  291.  
  292.       cmd[0] = 0;
  293.  
  294.       strcat(cmd, modname);
  295.       for(i=1, len=strlen(cmd); argv[i]; i++)
  296.       {
  297.          cmd[len] = ' ';
  298.          strcpy(cmd+len+1, argv[i]);
  299.          len += strlen(argv[i]) + 1;
  300.       }
  301.  
  302.       fh1 = Open("*", MODE_OLDFILE);
  303.       fh2 = Open("*", MODE_OLDFILE);
  304.  
  305.       //printf("GUIPROF: Executing command \"%s\"\n", cmd);
  306.  
  307.       if(SystemTags(cmd,  SYS_Input, fh1,
  308.                           SYS_Output, fh2, 
  309.                           SYS_Asynch, -1,
  310.                           SYS_UserShell, -1,
  311.                           TAG_DONE))
  312.       {
  313.          invoke_error:
  314.          fprintf(stderr, "Can't invoke command \"%s\"!\n", cmd);
  315.          DeletePort(port);
  316.          Close(fh1);
  317.          Close(fh2);
  318.          exit(20);
  319.       }
  320.       free(cmd);
  321.    }
  322.    //printf("GUIPROF: Waiting for process...\n");
  323.  
  324.    while(keepon)
  325.    {
  326.       res = SetSignal(0,SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_F);
  327.       if(broken || (res & SIGBREAKF_CTRL_C))
  328.       {
  329.          doctrlc:
  330.          if(process)
  331.          {
  332.             //printf("GUIPROF: Sending CTRL-C to process...\n");
  333.             Signal((struct Task *)process, SIGBREAKF_CTRL_C);
  334.             broken = 0;
  335.          }
  336.          else
  337.             break;
  338.       }
  339.       if(res & SIGBREAKF_CTRL_F)
  340.       {
  341.          doctrlf:
  342.          BUG(("CTRL-F received, setting up for report\n"))
  343.          if(forcereport)
  344.             Report(now);
  345.          else
  346.             doreport = 1;
  347.       }
  348.  
  349.       if(msg=(SPM)GetMsg(port))
  350.       {
  351.          switch(msg->flags)
  352.          {
  353.             case SPROF_INIT:
  354.                if(process == NULL) 
  355.                {
  356.                   BUG(("Accepting SPROF_INIT message, process 0x%08lx\n", msg->process))
  357.                   //printf("GUIPROF: Contacted process 0x%08lx\n", msg->process);
  358.                   process = msg->process;
  359.                   PostTimerReq(reporttime);
  360.                   DoTitle("Running");
  361.                }
  362.                else
  363.                {
  364.                   BUG(("Rejecting SPROF_INIT message, process 0x%08lx\n", msg->process))
  365.                   msg->flags = SPROF_DENIED;
  366.                }
  367.                break;
  368.  
  369.             case SPROF_ENTRY:
  370.                if(verbose)
  371.                   printf("GUIPROF: ENTRY timestamp %ld \"%s\"\n", msg->clk, 
  372.                          msg->id ? msg->id : "PROFILE_OFF");
  373.                if(spcur>=spmax)
  374.                {
  375.                   spmax += SPINCR;
  376.                   spdat = realloc(spdat, spmax*SIZSPDAT);
  377.                   if(!spdat)
  378.                   {
  379.                      keepon = 0;
  380.                      break;
  381.                   }
  382.                }
  383.                spdat[spcur].subrs = spdat[spcur].off = 0;
  384.                spdat[spcur].clk   = now = msg->clk;
  385.                spdat[spcur].id    = msg->id ? msg->id : nullid;
  386.                spcur++;
  387.                break;
  388.  
  389.             case SPROF_EXIT:
  390.                if(verbose)
  391.                   printf("GUIPROF: EXIT timestamp %ld \"%s\"\n", 
  392.                           msg->clk, msg->id ? msg->id : "PROFILE_ON");
  393.                if(msg->id == NULL) msg->id = nullid;
  394.                spdoexit(msg);
  395.                now = msg->clk;
  396.                break;
  397.             
  398.             case SPROF_TERM:
  399.             case SPROF_ABORT:
  400.                BUG(("SPROF_TERM/SPROF_ABORT\n"))
  401.                keepon = 0;
  402.                doreport = 1;
  403.                DoTitle("Complete");
  404.                break;
  405.  
  406.             default:
  407.                BUG(("Unknown message flags %d(0x%08lx\n", msg->flags, msg->flags))
  408.                msg->flags = SPROF_ABORT;
  409.                break;
  410.          }
  411.  
  412.          if(doreport)
  413.          {
  414.             Report(now);
  415.             doreport = 0;
  416.          }
  417.  
  418.          ReplyMsg(msg);
  419.       }
  420.       else
  421.       {
  422.          if(!process && verbose) printf("Waiting for process...\n");
  423.          res = Wait(waitbits);
  424.          if(res & SIGBREAKF_CTRL_C) goto doctrlc;
  425.          if(res & SIGBREAKF_CTRL_F) goto doctrlf;
  426.          if(res & timerbit)
  427.          {
  428.             BUG(("Timeout, setting up for report\n"))
  429.             if(forcereport)
  430.                Report(now);
  431.             else
  432.                doreport = 1;
  433.             GetTimerPkt(reporttime, 0);
  434.          }
  435.       }
  436.    }
  437.  
  438.    Forbid();
  439.    while(msg=(SPM)GetMsg(port))
  440.    {
  441.       msg->flags = SPROF_TERM;
  442.       ReplyMsg(msg);
  443.    }
  444.    DeletePort(port);
  445.    Permit();
  446.    port = NULL;
  447.  
  448.    return(0);
  449. }
  450.  
  451. static struct GPInfo *NewGPI(char *id)
  452. {
  453.    struct GPInfo *gpi;
  454.    char *funcname;
  455.  
  456.    assert(id != NULL);
  457.    
  458.    funcname = FuncName(id);
  459.    
  460.    gpi = malloc(sizeof(struct GPInfo) + strlen(funcname) + strlen(id) + 2);
  461.    if(!gpi)
  462.    {
  463.       fprintf(stderr, "Out of memory!\n");
  464.       exit(20);
  465.    }
  466.  
  467.    memset(gpi, 0, sizeof(*gpi));
  468.  
  469.    /* The copy of id here is only for sorting purposes.  We can't use    */
  470.    /* the value pointed to after this insertion because the program may  */
  471.    /* exit, but we can use the bit-pattern of the pointer as a unique    */
  472.    /* key on which to sort.                                              */
  473.    gpi->id = id;
  474.    gpi->name = (char *)(gpi+1);
  475.    strcpy(gpi->name, funcname);
  476.    gpi->fullname = gpi->name + strlen(gpi->name) + 1;
  477.    strcpy(gpi->fullname, id);
  478.  
  479.    return gpi;
  480. }
  481.  
  482. /* Note: This function assumes GPInfo is sorted by id. */
  483. struct GPInfo *FindGPI(struct GPInfo ***GPInfo_p, char *id,
  484.                        int *cur, int *tot)
  485. {
  486.    int i;
  487.    struct GPInfo **gpi;
  488.    struct GPInfo **GPInfo = *GPInfo_p;
  489.  
  490.    for(i=0; i<*cur && GPInfo[i]->id < id ; i++);
  491.  
  492.    if(i<*cur && GPInfo[i]->id == id)
  493.       return GPInfo[i];
  494.  
  495.    if(!tot) return NULL;
  496.  
  497.    /* Need to insert a new one right here */
  498.    if(*cur >= *tot)
  499.    {
  500.       *tot += 100;
  501.       if(!(gpi = malloc(*tot*sizeof(struct GPInfo *))))
  502.       {
  503.          fprintf(stderr, "Out of memory!\n");
  504.          exit(20);
  505.       }
  506.       if(i) memcpy(gpi, GPInfo, i*sizeof(struct GPInfo *));
  507.       gpi[i] = NewGPI(id);
  508.       if(i<*cur) memcpy(gpi+i+1, GPInfo+i, (*cur-i)*sizeof(struct GPInfo *));
  509.       if(GPInfo) free(GPInfo);
  510.       GPInfo = *GPInfo_p = gpi;
  511.       (*cur)++;
  512.    }
  513.    else
  514.    {
  515.       if(i < *cur)
  516.          memmove(GPInfo+i+1, GPInfo+i, (*cur-i)*sizeof(struct GPInfo *));
  517.       GPInfo[i] = NewGPI(id);
  518.       (*cur)++;
  519.    }
  520.    return GPInfo[i];
  521. }
  522.  
  523. static void spdoexit(SPM msg)
  524. {
  525.    sptime elapsed;
  526.    struct GPInfo *gpi;
  527.  
  528.    if(spcur <= 0)
  529.    {
  530.       /* Should never happen */
  531.       if(verbose) printf("GUIPROF: Call stack underflow! \"%s\"\n", msg->id);
  532.       return;
  533.    }
  534.  
  535.    /* Determine net elapsed time since function entry */
  536.    /* NOT COUNTING any time spent in PROFILE_OFF      */
  537.    spcur--;
  538.    elapsed = msg->clk - spdat[spcur].clk;
  539.  
  540.    if(msg->id == nullid)
  541.    {
  542.       /* PROFILE_ON() seen */
  543.       /* Adjust our parent's "off" time */
  544.       spdat[spcur-1].off += elapsed;
  545.       return;
  546.    }
  547.  
  548.    /* Adjust our parent's "subroutine elapsed" field */
  549.    spdat[spcur-1].subrs += elapsed - spdat[spcur].off;
  550.  
  551.    /* Adjust our parent's "off" field */
  552.    spdat[spcur-1].off += spdat[spcur].off;
  553.  
  554.    /* Now we need to associate the elapsed time with the function */
  555.    /* that is returning                                           */
  556.    gpi = FindGPI(&GPInfo, msg->id, &GPCur, &GPMax);
  557.  
  558.    gpi->count++;
  559.    gpi->time += elapsed - spdat[spcur].subrs - spdat[spcur].off;
  560.    gpi->tottime += elapsed - spdat[spcur].off;
  561.  
  562.    return;
  563. }
  564.  
  565. #if DODEBUG
  566. void bug(char *fmt, ...)
  567. {
  568.    va_list arg;
  569.    char buf[512];
  570.  
  571.    va_start(arg,fmt);
  572.    vsprintf(buf, fmt, arg);
  573.    va_end(arg);
  574.  
  575.    Write(Output(), buf, strlen(buf));
  576. }
  577. #endif
  578.